﻿////////////////////////////////////////////////////////////////////////////////////////////////
//
// Copyright © Yaroslavov Alexander 2010
//
// Contacts:
// Phone: +7(906)827-27-51
// Email: x-ronos@yandex.ru
//
/////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Moppet.Lapa
{



	/// <summary>
	/// Базовый класс. Здесь всё, что есть общего между LpsParser и LpmParser.
	/// </summary>
	/// <typeparam name="TDerived">Производный от данного класс.</typeparam>
	public class LpParserAttrs<TDerived> : ICloneable where TDerived : LpParserAttrs<TDerived>, new()
	{
		/// <summary>
		/// Истина, если необходимо оборачивать узел (результат, возвращаемый парсером), если у него ненулевой идентификатор.
		/// </summary>
		protected bool m_wrapNode = false;

		/// <summary>
		/// Идентификатор.
		/// </summary>
		protected string m_identifier = null;


		/// <summary>
		/// Истина, если необходимо оборачивать узел (результат, возвращаемый парсером), если у него ненулевой идентификатор.
		/// </summary>
		public bool WrapNode { get { return m_wrapNode; } }

		/// <summary>
		/// Идентификатор.
		/// </summary>
		public string Identifier { get { return m_identifier; } }


        /// <summary>
		/// Создаёт копию парсера с новым идентификатором.
		/// </summary>
		/// <param name="id">Идентификатор.</param>
		/// <returns>Клон с новым идентификатором.</returns>
        public TDerived Id(string id)
        {
            return Id(id: id, wrap: this.m_wrapNode);
        }

		/// <summary>
		/// Создаёт копию парсера с новым идентификатором.
        /// Свойство WrapNode регулируется аргументом wrap.
		/// </summary>
		/// <param name="id">Идентификатор.</param>
		/// <param name="wrap">Истина, если необходимо оборачивать узел (результат, возвращаемый парсером), если у него ненулевой идентификатор.</param>
		/// <returns>Клон с новым идентификатором.</returns>
		public TDerived Id(string id, bool wrap)
		{
            if (m_identifier != null && m_identifier != id)
                throw new ArgumentException("Parser has another identifier already: " + m_identifier, "id");

			LpParserAttrs<TDerived> copy = Copy();
			copy.m_identifier = id;
			copy.m_wrapNode = wrap;
			return (TDerived)copy;
		}

        /// <summary>
        /// Делает то же самое, что и ф-я Id, но при этом не выбрасывает исключение, позволяя переназначить идентификатор.
        /// Создаёт копию парсера с новым идентификатором.
        /// </summary>
        /// <param name="newId">Новый идентификатор.</param>
        /// <returns>Клон с новым идентификатором.</returns>
        public TDerived Rename(string newId)
        {
            return Rename(newId: newId, wrap: this.m_wrapNode);
        }

        /// <summary>
        /// Делает то же самое, что и ф-я Id, но при этом не выбрасывает исключение, позволяя переназначить идентификатор.
        /// Создаёт копию парсера с новым идентификатором.
        /// Свойство WrapNode регулируется аргументом wrap.
        /// </summary>
        /// <param name="newId">Новый идентификатор.</param>
        /// <param name="wrap">Истина, если необходимо оборачивать узел (результат, возвращаемый парсером), если у него ненулевой идентификатор.</param>
        /// <returns>Клон с новым идентификатором.</returns>
        public TDerived Rename(string newId, bool wrap)
        {
            LpParserAttrs<TDerived> copy = Copy();
            copy.m_identifier = newId;
            copy.m_wrapNode = wrap;
            return (TDerived)copy;
        }


		/// <summary>
		/// Типизированное клонирование объекта.
		/// </summary>
		/// <returns>Клон.</returns>
		public virtual TDerived Copy()
		{
			TDerived c = new TDerived();
			c.m_identifier = this.Identifier;
			c.m_wrapNode   = this.WrapNode;
			return c;
		}

		/// <summary>
		/// Identifier ?? GetType().Name;
		/// </summary>
		/// <returns>Информация о парсере.</returns>
		public override string ToString()
		{
			return Identifier ?? GetType().Name;
		}

		/// <summary>
		/// Клонирование объекта.
		/// </summary>
		/// <returns>Клон.</returns>
		public object Clone()
		{
			return Copy();
		}
	}

	/// <summary>
	/// Базовый класс. Здесь всё, что есть общего между LpsParser и LpmParser.
    /// Обратите внимание на тип TDerived, таким образом мы реализуем типизированный для TDerived функционал в базовом классе.
	/// </summary>
	/// <typeparam name="TResult">Результат парсера.</typeparam>
	/// <typeparam name="TDerived">Производный от данного класс.</typeparam>
	public class LpBaseParser<TResult, TDerived> : LpParserAttrs<TDerived>
		
		where TDerived : LpBaseParser<TResult, TDerived>, new()
	{
		/// <summary>
		/// Противозацикливатель. В копировании не участвует и не должен.
		/// </summary>
		protected LinkedList<LpText> m_stack = null;

		/// <summary>
		/// Парсер.
		/// </summary>
		protected Func<LpText, TResult> m_parser = null;

		/// <summary>
		/// Парсер - лямбда.
		/// Это свойство предназначено исключиетльно для единичной инициализации и внешнего доступа.
		/// </summary>
		public Func<LpText, TResult> Parser
		{
			get { return m_parser; }
			set
			{
				if (m_parser != null)
					throw new ArgumentException("Property \"Parser\" already initialized.");
				m_parser = value;
			}
		}

		/// <summary>
		/// Опция контроля за рекурсией, контроля за переполнением стека.
		/// Это пользовательское свойство.
		/// </summary>
		public bool Recurse
		{
			get { return m_stack != null; }
			set
			{
				if (m_stack != null)
                    throw new ArgumentException("The 'Recurse' property already initialized.");
				m_stack = value ? new LinkedList<LpText>() : null;
			}
		}

		/// <summary>
		/// Типизированное клонирование объекта.
		/// </summary>
		/// <returns>Клон.</returns>
		public override TDerived  Copy()
		{
			if (m_parser == null)
			{
				if (Identifier != null)
					throw new NullReferenceException(string.Format("The parser named '{0}' has not been initialized completely.", Identifier));
				else
                    throw new NullReferenceException("The 'Parser' property has not been initialized.");
			}
			LpBaseParser<TResult, TDerived> c = base.Copy();
			c.m_parser = this.m_parser;
			return (TDerived)c;
		}
	}
}
